package spring;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ProjectName: Spring
 * @Package: spring
 * @ClassName: DefaultListableBeanFactory
 * @Author: zxh
 * @Description: 用于对象的创建
 * @Date: 2022/3/10 15:18
 * @Version: 1.0
 */
public class DefaultListableBeanFactory implements BeanDefinitionRegistry {

    /**
     * 单例池
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 用于未完成创建的对象的存储
     */
    private ConcurrentHashMap<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    /**
     * 用于创建未完成对象的创建的工厂的存储
     */
    private ConcurrentHashMap<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();

    /**
     * 用于保证BeanName的唯一性
     */
    private HashSet<String> beanDefinitionNames = new HashSet<>();

    /**
     * 用于存储BeanDefinition
     */
    public ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 存储beanPostProcessor
     */
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    /**
     * 表明该对象至少被创建过一次
     */

    private HashSet<String> alreadyCreated = new HashSet<>();

    ClassLoader loader;

    // 是否运行循环依赖
    boolean allowCircularReferences = true;

    /***
     * 保证对象仅仅创建一次代理
     */
    private HashSet<String> exposedObjectSet = new HashSet<>();

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
        beanDefinitionNames.add(beanName);
    }

    @Override
    public void removeBeanDefinition(String beanName) {
        beanDefinitionMap.remove(beanName);
        beanDefinitionNames.remove(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        BeanDefinition bd = null;
        if (containsBeanDefinition(beanName)) {
            bd = beanDefinitionMap.get(beanName);
        }
        return bd;
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionNames.contains(beanName);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        String[] arr = new String[getBeanDefinitionCount()];
        int index = 0;
        for (String beanDefinitionName : beanDefinitionNames) {
            arr[index++] = beanDefinitionName;
        }
        return arr;
    }

    public ArrayList<String> getBeanNamesForType(Class objClass) {
        ArrayList<String> list = new ArrayList<>();
        for (String key : beanDefinitionMap.keySet()) {
            BeanDefinition value = beanDefinitionMap.get(key);
            if (objClass.isAssignableFrom(value.getClazz())) {
                list.add(key);
            }
        }
        return list;
    }

    public Object getBean(String name) {
        return doGetBean(name);
    }

    private Object doGetBean(String beanName) {
        if (!beanDefinitionNames.contains(beanName)) {
            throw new NullPointerException("这个对象未注册");
        }
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        markBeanAsCreated(beanName);
        Object bean;
        if (bd.isSingleton()) {
            bean = getSingleton(beanName);
            if (bean == null) {
                bean = doCreateBean(new BeanDefinitionHolder(bd, beanName));
            }
        } else if (bd.isPrototype()) {
            bean = doCreateBean(new BeanDefinitionHolder(bd, beanName));
        } else {
            throw new NullPointerException("未知的scope");
        }
        return bean;
    }

    private Object doCreateBean(BeanDefinitionHolder beanDefinitionHolder) {
        BeanDefinition bd = beanDefinitionHolder.getBeanDefinition();
        String beanName = beanDefinitionHolder.getBeanName();
        Object instance = null;
        try {
            Class clazz = bd.getClazz();
            instance = createBeanInstance(clazz);
            final Object bean = instance;
            boolean earlySingletonExposure = (bd.isSingleton() && this.allowCircularReferences);
            // 提前暴露bean
            if(earlySingletonExposure){
                addSingletonFactory(beanName, getEarlyBeanReference(beanName, bean));
            }
            populateBean(instance, clazz);
            instance = initializeBean(beanName, instance);
            addSingleton(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 初始化Bean
     *
     * @param beanName                Id
     * @param instance                bean实例
     * @return 初始化后的对象
     */
    private Object initializeBean(String beanName, Object instance) throws Exception {
        /*进行Aware有关设置，BeanFactoryAware主要用于解决单例对象在注入过程中失效的情况,BeanNameAware应该没啥用可能*/
        invokeAwareMethods(beanName, instance);
        /*实现beanPostProcessor接口的类允许在初始化前进行一些操作*/
        instance = applyBeanPostProcessorsBeforeInitialization(beanName, instance);
        /*执行对象的初始化方法*/
        invokeInitMethods(instance);
        /*实现beanPostProcessor接口的类允许在初始化后进行一些操作，同时代理对象的创建也在这个方法之中*/
        instance = applyBeanPostProcessorsAfterInitialization(beanName, instance);
        return instance;
    }

    /**
     * 进行Aware有关设置
     *
     * @param beanName Id
     * @param instance bean实例
     */
    private void invokeAwareMethods(String beanName, Object instance) {
        if (instance instanceof BeanFactoryAware) {
            ((BeanFactoryAware) instance).setBeanFactory(this);
        }
        if (instance instanceof BeanNameAware) {
            ((BeanNameAware) instance).setBeanName(beanName);
        }
    }

    /**
     * 初始化前的beanPostProcessor
     *
     * @param beanName Id
     * @param instance Bean实例
     * @return bean
     */
    private Object applyBeanPostProcessorsBeforeInitialization(String beanName, Object instance) {
        Object AfterPostBean = instance;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof AopBeanPostProcessor) {
                continue;
            }
            AfterPostBean = beanPostProcessor.postProcessBeforeInitialization(AfterPostBean, beanName);
        }
        return AfterPostBean;
    }

    /**
     * 执行bean的初始化方法
     *
     * @param instance
     */
    private void invokeInitMethods(Object instance) throws Exception {
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }
    }

    /**
     * 初始化后的beanPostProcessor
     *
     * @param beanName Id
     * @param instance Bean实例
     * @return bean
     */
    private Object applyBeanPostProcessorsAfterInitialization(String beanName, Object instance) {
        boolean exposedBean = exposedObjectSet.contains(beanName);
        Object AfterPostBean = instance;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (exposedBean && (beanPostProcessor instanceof AopBeanPostProcessor)) {
                /*保证代理对象仅仅创建一次*/
                continue;
            }
            AfterPostBean = beanPostProcessor.postProcessAfterInitialization(AfterPostBean, beanName);
        }
        return AfterPostBean;
    }



    /**
     * 实现注入
     * @param instance 传入Bean实例
     * @param clazz    传入Class
     * @throws IllegalAccessException
     */
    private void populateBean(Object instance, Class clazz) throws IllegalAccessException {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if(beanPostProcessor instanceof AutowiredAnnotationBeanPostProcessor){
                AutowiredAnnotationBeanPostProcessor bpp = (AutowiredAnnotationBeanPostProcessor)beanPostProcessor;
                bpp.postProcessProperties(instance,clazz,this);
            }
        }
    }

    /**
     * 用于存储创建对象的工厂
     * @param beanName
     * @param singletonFactory
     */
    public void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }

    /**
     * 用于存储单例对象
     * @param beanName
     * @param singletonObject
     */
    public void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    private ObjectFactory getEarlyBeanReference(String beanName, Object instance) {
        Object exposedObject = instance;

        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if(bp instanceof AbstractAutoProxyCreator){
                exposedObject = bp.postProcessAfterInitialization(exposedObject, beanName);
            }
        }
        return new DefaultObjectFactory(exposedObject);

    }

    private Object createBeanInstance(Class clazz) throws Exception{
        return clazz.getDeclaredConstructor().newInstance();
    }

    public <T> T getBean(String name, Class<T> requiredType) {
        return (T) getBean(name);
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionNames.size();
    }

    @Override
    public boolean isBeanNameInUse(String beanName) {
        return beanDefinitionNames.contains(beanName);
    }

    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.add(beanPostProcessor);
    }

    public void setLoader(ClassLoader loader) {
        this.loader = loader;
    }

    public ClassLoader getLoader() {
        return loader;
    }

    public void preInstantiateSingletons() {

        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.isSingleton()) {
                getBean(beanName);
            }
        }
    }

    /**
     * 获取单例对象
     *
     * @param beanName
     * @return
     */
    public Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null) {
                    ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }


    protected void markBeanAsCreated(String beanName) {
        if (!this.alreadyCreated.contains(beanName)) {
            synchronized (this.alreadyCreated) {
                if (!this.alreadyCreated.contains(beanName)) {
                    this.alreadyCreated.add(beanName);
                }
            }
        }
    }

    public List<BeanPostProcessor> getBeanPostProcessors(){
        return beanPostProcessors;
    }


}
